/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.query.ast;

import java.util.ArrayList;
import java.util.List;

import edu.ucsd.db.bassarisk.exception.VisitorException;
import edu.ucsd.db.bassarisk.query.QueryExpression;
import edu.ucsd.db.bassarisk.query.TransformReturn;

public class SyntaxExpressions {
  public static class NonterminalSyntaxExpression extends SyntaxExpression {
    private final ArrayList<String> args;
    private final String name;
    private final ArrayList<SyntaxExpression> subExprs;

    public NonterminalSyntaxExpression(final String name, final List<String> args,
                                       final List<SyntaxExpression> subExprs) {
      this.args = new ArrayList<String>();
      this.subExprs = new ArrayList<SyntaxExpression>();
      this.name = name;
      this.args.addAll(args);
      this.subExprs.addAll(subExprs);
    }

    @Override
    public <R, A> R accept(final SyntaxExpressionVisitor<R, A> visitor, final A arg)
        throws VisitorException {
      return visitor.visitNonterminalAbstractExpression(this, arg);
    }

    public List<String> getArguments() {
      return this.args;
    }

    @Override
    public String getName() {
      return this.name;
    }

    public List<SyntaxExpression> getSubExpressions() {
      return this.subExprs;
    }
  }

  public abstract static class SyntaxExpression implements QueryExpression,
      TransformReturn<SyntaxExpression> {
    private int startColumn;
    private int startLine;

    private SyntaxExpression() {}

    public abstract <R, A> R accept(SyntaxExpressionVisitor<R, A> visitor, A arg)
        throws VisitorException;

    @SuppressWarnings("unchecked")
    @Override
    public <V, R, A> R accept(final V visitor, final A args) throws VisitorException {
      if (visitor instanceof SyntaxExpressionVisitor<?, ?>) {
        return this.accept((SyntaxExpressionVisitor<R, A>) visitor, args);
      } else {
        throw VisitorException.invalidVisitorType();
      }
    }

    @Override
    public int getStartColumn() {
      return this.startColumn;
    }

    @Override
    public int getStartLine() {
      return this.startLine;
    }

    @Override
    public SyntaxExpression getTransformationRoot() {
      return this;
    }

    void setDebugInfo(final int startLine, final int startColumn) {
      this.startLine = startLine;
      this.startColumn = startColumn;
    }
  }

  public static class TerminalSyntaxExpression extends SyntaxExpression {
    private final boolean isReferenced;
    private final String reference;
    private final String token;

    private TerminalSyntaxExpression(final String token, final boolean isReferenced,
                                     final String reference) {
      this.token = token;
      this.isReferenced = isReferenced;
      this.reference = reference;
    }

    @Override
    public <R, A> R accept(final SyntaxExpressionVisitor<R, A> visitor, final A arg)
        throws VisitorException {
      return visitor.visitTerminalAbstractExpression(this, arg);
    }

    @Override
    public String getName() {
      return "VALUE";
    }

    public String getReference() {
      return this.reference;
    }

    public String getToken() {
      return this.token;
    }

    public boolean isReferenced() {
      return this.isReferenced;
    }
  }

  public static SyntaxExpression createKeywordExpression(final String token,
                                                         final boolean named,
                                                         final String name,
                                                         final int startColumn,
                                                         final int startLine) {
    SyntaxExpression newExpr = new TerminalSyntaxExpression(token, named, name);
    newExpr.setDebugInfo(startLine, startColumn);
    return newExpr;
  }

  public static NonterminalSyntaxExpression
      createNonterminalExpression(final String name, final int startLine,
                                  final int startColumn, final List<String> args,
                                  final List<SyntaxExpression> subExprs) {
    NonterminalSyntaxExpression newExpr =
        new NonterminalSyntaxExpression(name, args, subExprs);
    newExpr.setDebugInfo(startLine, startColumn);
    return newExpr;
  }
}
